package com.alexii.j2v8debugger

import com.alexii.j2v8debugger.utils.logger
import com.facebook.stetho.inspector.console.RuntimeReplFactory
import com.facebook.stetho.inspector.jsonrpc.JsonRpcException
import com.facebook.stetho.inspector.jsonrpc.JsonRpcPeer
import com.facebook.stetho.inspector.jsonrpc.JsonRpcResult
import com.facebook.stetho.inspector.protocol.ChromeDevtoolsDomain
import com.facebook.stetho.inspector.protocol.ChromeDevtoolsMethod
import org.json.JSONArray
import org.json.JSONObject
import java.util.concurrent.Callable
import java.util.concurrent.ExecutorService
import com.facebook.stetho.inspector.protocol.module.Runtime as FacebookRuntimeBase

/**
 * Runtime Domain. Name of the class and methods must match names defined in Chrome Dev Tools protocol.
 */
@Suppress("UNUSED_PARAMETER", "unused")
class Runtime(replFactory: RuntimeReplFactory?) : ChromeDevtoolsDomain {
    private var v8Executor: ExecutorService? = null
    private var v8Messenger: V8Messenger? = null
    private val adaptee = FacebookRuntimeBase(replFactory)

    fun initialize(v8Executor: ExecutorService, v8Messenger: V8Messenger) {
        this.v8Executor = v8Executor
        this.v8Messenger = v8Messenger
    }

    @ChromeDevtoolsMethod
    fun enable(peer: JsonRpcPeer?, params: JSONObject?) {
        logger.d("Runtime", "enable : $params")
        v8Executor?.submit {
            v8Messenger?.sendMessage(Protocol.Runtime.Enable)
        }
    }

    @ChromeDevtoolsMethod
    fun addBinding(peer: JsonRpcPeer?, params: JSONObject?) {
        logger.d("Runtime", "addBinding : $params")
        v8Executor?.submit {
            v8Messenger?.sendMessage(Protocol.Runtime.AddBinding, params)
        }
    }

    @ChromeDevtoolsMethod
    fun runIfWaitingForDebugger(peer: JsonRpcPeer?, params: JSONObject?): JsonRpcResult {
        logger.d("Runtime", "runIfWaitingForDebugger : $params")
        v8Executor?.submit {
            v8Messenger?.sendMessage(Protocol.Runtime.RunIfWaitingForDebugger)
        }
        return CommonJsonResult()
    }

    @ChromeDevtoolsMethod
    fun getProperties(peer: JsonRpcPeer?, params: JSONObject?): JsonRpcResult {
        logger.d("Runtime", "getProperties: $params")
        val method = Protocol.Runtime.GetProperties
        val result = v8Executor?.submit(Callable {
            v8Messenger?.getV8Result(method, params)
        })?.get()
        val jsonResult = try {
            GetPropertiesResult().put("result", JSONArray(result))
        } catch (e: Exception) {
            val error = JSONObject(result)
            GetPropertiesResult().apply {
                put("code", error.optInt("code"))
                put("message", error.optString("message"))
            }
        }
        return jsonResult as JsonRpcResult
    }

    /**
     * Pass through the needed [FacebookRuntimeBase] methods
     */
    @ChromeDevtoolsMethod
    fun releaseObject(peer: JsonRpcPeer?, params: JSONObject?) = adaptee.releaseObject(peer, params)

    @ChromeDevtoolsMethod
    fun releaseObjectGroup(peer: JsonRpcPeer?, params: JSONObject?) = adaptee.releaseObjectGroup(peer, params)

    @ChromeDevtoolsMethod
    fun callFunctionOn(peer: JsonRpcPeer?, params: JSONObject?): JsonRpcResult? = adaptee.callFunctionOn(peer, params)

    @ChromeDevtoolsMethod
    fun compileScript(peer: JsonRpcPeer?, params: JSONObject?): JsonRpcResult? {
        logger.d("Runtime", "compileScript$params")
//        val method = Protocol.Runtime.CompileScript
//        val result = v8Executor?.submit(Callable {
//            v8Messenger?.getV8Result(method, params)
//        })?.get()
        return CommonJsonResult()
    }

    @ChromeDevtoolsMethod
    fun globalLexicalScopeNames(peer: JsonRpcPeer?, params: JSONObject?): JsonRpcResult? {
        logger.d("Runtime", "GlobalLexicalScopeNames$params")
        val method = Protocol.Runtime.GlobalLexicalScopeNames
        val result = v8Executor?.submit(Callable {
            v8Messenger?.getV8Result(method, params)
        })?.get()
        return CommonJsonResult().apply {
            put("names", JSONObject(result).optJSONArray("names"))
        }
    }

    @ChromeDevtoolsMethod
    fun evaluate(peer: JsonRpcPeer?, params: JSONObject?): JsonRpcResult {
        logger.d("Runtime", "evaluate$params")
        return adaptee.evaluate(peer, params)
    }
}
